package com.nondo.dean.platform.service;

import java.util.UUID;
import java.util.concurrent.TimeUnit;

import org.apache.commons.lang3.StringUtils;
import org.jose4j.base64url.Base64Url;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.nondo.dean.common.tool.BeanUtils;
import com.nondo.dean.platform.domain.Customer;
import com.nondo.dean.platform.model.SessionBean;
import com.nondo.dean.platform.model.WeChatAppLoginReq;
import com.nondo.dean.platform.model.WeChatAppLoginReq.RawData;
import com.nondo.dean.platform.tool.HmacUtil;
import com.nondo.dean.platform.tool.WeChatConfig;

/**
 * 微信小程序登录
 * 
 * @author zhaolijin
 * @date 2018年5月7日
 */
@Component
public class WeChatAppMiniService {
    private static final Logger logger = LoggerFactory.getLogger(WeChatAppMiniService.class);
    
    /**
     * 有效期30天
     */
    private static final long EXPIRE_TIME = 30 * 24 * 3600;
    
    @Autowired
    private CustomerService customerService;

    @SuppressWarnings("rawtypes")
    @Autowired
    private RedisTemplate redisTemplate;

    public SessionBean login(WeChatAppLoginReq req) {
        // 获取 session_key 和 openId
        String url =
            "https://api.weixin.qq.com/sns/jscode2session?appid=" + WeChatConfig.APPID + "&secret=" + WeChatConfig.SECRET + "&js_code="
                + req.getCode() + "&grant_type=authorization_code";
        RestTemplate restTemplate = new RestTemplate();
        ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, null, String.class);
        if (responseEntity != null && responseEntity.getStatusCode() == HttpStatus.OK) {
            String sessionData = responseEntity.getBody();
            logger.info("sessionData = " + sessionData);
            
            JSONObject jsonObj = JSON.parseObject(sessionData);
            if (jsonObj.getString("errcode") != null) {
                throw new RuntimeException("无效CODE");
            }
            String openid = jsonObj.getString("openid");
            String unionid = jsonObj.getString("unionid");
            String sessionKey = jsonObj.getString("session_key");
            logger.info(" openid= " + openid);
            logger.info(" unionid= " + unionid);
            logger.info(" sessionKey= " + sessionKey);

            //validateSignature(req, sessionKey);
            
            RawData rawData = req.getRawData();
            
            Customer customer = customerService.selectCustomerByOpenid(openid);
            if (customer == null) {
                customer = new Customer();
                BeanUtils.copy(rawData, customer);
                customer.setOpenid(openid);
                customer.setUnionid(unionid);
                customerService.addCustomer(customer);
            } else {
                customer.setOpenid(openid);
                customer.setUnionid(unionid);
                customerService.updateCustomer(customer);
            }
            SessionBean sessionBean = null;
            sessionBean = storeSessionIdToRedis(openid, sessionKey, customer.getId());
            sessionBean.setPhone(customer.getPhone());
            sessionBean.setRemarks(customer.getRemarks());
            return sessionBean;

        } else {
            throw new RuntimeException("微信登录CODE错误");
        }
    }

    /**
     * 微信签名验证
     * @param req
     * @param sessionKey
     */
    public void validateSignature(WeChatAppLoginReq req, String sessionKey) {
        String signature = HmacUtil.SHA1(req.getRawData() + sessionKey);
        if (!signature.equals(req.getSignature())) {
            logger.info(" req signature=" + req.getSignature());
            logger.info(" java signature=" + signature);
            throw new RuntimeException("微信登录签名错误");
        }
        
        byte[] resultByte = null;
        try {
            resultByte =
                HmacUtil.decrypt(Base64Url.decode(req.getEncryptedData()), Base64Url.decode(sessionKey),
                    Base64Url.decode(req.getIv()));
        } catch (Exception e) {
            throw new RuntimeException("微信登录用户错误");
        }
        if (null == resultByte || resultByte.length == 0) {
            throw new RuntimeException("微信登录用户错误");
        }
    }

    /**
     * 将openid和sessionKey放入redis缓存
     * @param openid
     * @param sessionKey
     * @param customerId 
     * @return SessionBean
     */
    @SuppressWarnings("unchecked")
    public SessionBean storeSessionIdToRedis(String openid, String sessionKey, Integer customerId) {
        SessionBean session = new SessionBean();
        session.setOpenid(openid);
        session.setSessionKey(sessionKey);
        session.setCustomerId(customerId);
        ValueOperations<String, SessionBean> valueOperations = redisTemplate.opsForValue();
        if (valueOperations.get(openid) != null) {
            redisTemplate.delete(openid);
        }
        String sessionId = UUID.randomUUID().toString();
        session.setSessionId(sessionId);
        valueOperations.set(openid, session);
        valueOperations.set(sessionId, session, EXPIRE_TIME, TimeUnit.SECONDS);
        return session;
    }
    
    /**
     * 校验登录用户session
     * @param sessionId
     * @return session 包含openid和sessionKey
     */
    public SessionBean checkSession(String sessionId) {
        if (StringUtils.isBlank(sessionId))
            return null;
        SessionBean session = new SessionBean();
        @SuppressWarnings("unchecked")
        ValueOperations<String, SessionBean> valueOperations = redisTemplate.opsForValue();
        if (valueOperations.get(sessionId) != null) {
            session = valueOperations.get(sessionId);
            return session;
        }
        return null;
    }

    /**
     * 开启和停止
     * 
     * @param type 0=停止 1=开始  2=查询
     */
    public Integer startAndStopShake(Integer type) {
        @SuppressWarnings("unchecked")
        ValueOperations<String, Integer> valueOperations = redisTemplate.opsForValue();
        if (type == WeChatConfig.GET_SHAKE) {
            return valueOperations.get(WeChatConfig.REDIS_SHAKE_EKY) != null ? valueOperations
                .get(WeChatConfig.REDIS_SHAKE_EKY) : WeChatConfig.STOP_SHAKE;
        } else if (type == WeChatConfig.START_SHAKE) {
            valueOperations.set(WeChatConfig.REDIS_SHAKE_EKY, WeChatConfig.START_SHAKE, EXPIRE_TIME, TimeUnit.SECONDS);
            return WeChatConfig.START_SHAKE;
        } else if (type == WeChatConfig.STOP_SHAKE) {
            valueOperations.set(WeChatConfig.REDIS_SHAKE_EKY, WeChatConfig.STOP_SHAKE, EXPIRE_TIME, TimeUnit.SECONDS);
            customerService.cleanShakeNum();
            return WeChatConfig.STOP_SHAKE;
        }
        return WeChatConfig.STOP_SHAKE;
    }

    /**
     * 累计摇动次数
     * @param shakeNum 摇动次数
     * @return 增加后的次数
     */
    @Transactional
    public Integer addShakeNum(Integer customerId, Integer shakeNum) {
        Customer customer = customerService.selectCustomer(customerId);
        customer.setShakeNum(shakeNum);
        customerService.updateCustomer(customer);
        return customer.getShakeNum();
    }
    
}
